Pesquisa — AS-IS: NewSellStepper e arquitetura de steps do fluxo de venda
Data: 2026-05-27
Contexto: Task #19323 — Atualizar componente de steps do fluxo de venda para step Upsell condicional
Estado Atual do Código
Componente NewSellStepper
Arquivo: coezzion_vendas_app/lib/screens/sell/new_sell/widgets/new_sell_stepper.dart
- Classe:
NewSellStepper extends StatelessWidget— puramente apresentacional, sem estado reativo. - Construtor:
const NewSellStepper(this.listStatus, {super.key})— recebeList<StatusStepp>com exatamente 3 posições. - Renderização:
Rowcom 3_ItemSteppintercalados por 2_SteppDivider. Cada_ItemStepprenderiza um círculo com:- Ícone de check (verde) se
StatusStepp.complete - Círculo preenchido com número se
StatusStepp.selected - Círculo com borda se
StatusStepp.unselected
- Ícone de check (verde) se
- Controllers: Zero dependências. Não acessa
getIt. Não usaAppGetBuilder. - Dependências:
StepperModel,StatusStepp,SteppSell,PhosphorIcons,ZZColors,ZZTextNew,ColorThemeUtils.
Enums e modelos
Arquivo: coezzion_vendas_app/lib/shared/enum/stepp_sell.dart
enum SteppSell { produto, cliente, conferencia }
final stepperSellProduct = [StatusStepp.selected, StatusStepp.unselected, StatusStepp.unselected];
final stepperSellCustomer = [StatusStepp.complete, StatusStepp.selected, StatusStepp.unselected];
final stepperSellCheck = [StatusStepp.complete, StatusStepp.complete, StatusStepp.selected];
Arquivo: coezzion_vendas_app/lib/shared/enum/status_stepp.dart
enum StatusStepp { complete, selected, unselected }
Arquivo: coezzion_vendas_app/lib/models/stepper/stepper.dart
class StepperModel {
final int number;
final String title;
final StatusStepp status;
final SteppSell steppSell;
factory StepperModel.productStepperModel(status) => StepperModel(number: 1, title: 'Produtos', ...);
factory StepperModel.clienteStepperModel(status) => StepperModel(number: 2, title: 'Cliente', ...);
factory StepperModel.conferenciaStepperModel(status) => StepperModel(number: 3, title: 'Conferir', ...);
}
Consumo do stepper — 7 telas
| Tela | Rota | Array passado | Quando |
|---|---|---|---|
NewSellScreen | /new_sell | stepperSellProduct ou stepperSellCustomer | NewSellType de Get.arguments |
AddProductsScreen | /add_products | stepperSellProduct | Sempre (dentro de AppGetBuilder<ProductController>) |
CheckProductsScreen | /check_products | stepperSellProduct | Condicional (!checkSell) |
AddCustomerScreen | /add_sell_customer | stepperSellCustomer | Sempre (dentro de PagingListener) |
CheckCustomerScreen | /check_customer | stepperSellCustomer | Sempre |
CheckAddressCustomerScreen | /check_customer_address | stepperSellCustomer | Condicional (!checkSell) |
SelectDeliveryMethodScreen | /select_delivery_method | stepperSellCustomer | Sempre |
CheckSellScreen | /check_sell | stepperSellCheck | Sempre (dentro de AppGetBuilder<CartController>) |
Observação: O stepper NÃO é reativo. A ilusão de mudança entre steps vem exclusivamente da navegação entre telas — cada tela instancia seu próprio NewSellStepper com um array estático diferente. O estado do stepper é determinado por rota, não por estado observável.
Mecanismo de reatividade do projeto
O projeto usa GetX com a seguinte cadeia reativa:
OpenSalesController.activeCartDTO—Rxn<CartDTO?>(fonte da verdade)CartController— subscreve-se viaaddListenere chamaupdate()(GetxController)AppGetBuilder<T>— widget customizado que subscreve-se ao controller e chamasetState()
Mecanismo de feature flag
- Serviço:
FirebaseRemoteConfigService— classe abstrata com membros estáticos - Acesso:
FirebaseRemoteConfigService.dictionary.algumaFlag— síncrono, sem DI - Enum:
FirebaseRemoteConfigKeysEnum— ~48 flags existentes - Pattern:
{flag}(bool) +{flag}_by_code_store(List<String>separado por;) para rollout gradual por loja - Flags planejadas (task 07):
enableUpsellRecommendation+enableUpsellRecommendationByCodeStore— ainda não implementadas no código
Determinação de saleEcommerce
Arquivo: coezzion_vendas_app/lib/models/cart/cart_dto.dart (linha 385)
CartDTO addItem({Product? product, ...}) {
final saleEcommerceT = saleEcommerce ?? product.sizesStockStore.isEmpty;
return copyWith(saleEcommerce: Value(saleEcommerceT), ...);
}
- Lock-in:
??— uma vez definido, nunca muda até o carrinho esvaziar - null: carrinho vazio
- false: estoque loja (fluxo store — candidato a upsell)
- true: ecommerce ("Prateleira infinita" — NÃO candidato a upsell)
Fluxo de navegação entre steps
StructureSellerController.onTapNewSell()
└── OpenSalesController.createNewCartDTO(saleEcommerce: null)
└── Get.toNamed(AppRoutes.newSell, arguments: NewSellType.product)
└── NewSellScreen (stepperSellProduct)
├── "Bipar produto" → AddProductsScreen (stepperSellProduct)
│ └── "Conferir" → CheckProductsScreen (stepperSellProduct, !checkSell)
│ └── "Adicionar cliente" → NewSellScreen (stepperSellCustomer)
│ └── "Buscar cliente" → AddCustomerScreen (stepperSellCustomer)
│ └── Select → CheckCustomerScreen (stepperSellCustomer)
│ └── "Avançar" → CheckAddressCustomerScreen (stepperSellCustomer, !checkSell)
│ └── SelectDeliveryMethodScreen (stepperSellCustomer)
│ └── CheckSellScreen (stepperSellCheck)
Decisões tomadas
| # | Questão | Decisão |
|---|---|---|
| Q1 | O stepper atual é reativo? | Não — é puramente estático, cada tela renderiza seu próprio widget com array fixo. A mudança visual vem da navegação entre telas. |
| Q2 | Onde travar saleEcommerce? | No CartDTO.addItem() via ?? — primeiro item define o tipo do carrinho. Não muda até o carrinho esvaziar. |
| Q3 | Como as feature flags são acessadas? | Estaticamente via FirebaseRemoteConfigService.dictionary.* — síncrono, sem DI. Padrão estabelecido em 12+ controllers existentes. |
| Q4 | Qual o mecanismo de DI? | GetIt (get_it package) com wrappers customizados registerLazySingleton e registerFactory. Controllers compartilhados usam registerLazySingleton. |
| Q5 | Quantas telas consomem o stepper? | 7 telas em lib/screens/sell/new_sell/. Nenhuma delas usa estado reativo para o stepper — todas passam arrays estáticos. |
| Q6 | CartController tem capacidade para mais responsabilidade? | Não — 768 linhas, 55+ métodos, já gerencia produto, CRM, desconto, frete, estoque, cliente, delivery, shipment, validação de limites. |